package com.ifp.opengate.boot.filter;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.ifp.adapter.common.AdapterCanstants;
import com.ifp.core.context.ClogicContext;
import com.ifp.core.log.Trace;
import com.ifp.opengate.boot.constants.Constants;
import com.ifp.opengate.boot.emum.ErrorEnum;
import com.ifp.opengate.boot.exception.AccessControlException;
import com.ifp.opengate.boot.utils.RedisUtils;
import com.ifp.opengate.boot.utils.RequestUtil;

import reactor.core.publisher.Mono;

/**
 * @author liwq
 * @description 黑白名单全局过滤器
 * @date 2021/8/5 17:49
 */
@Component
public class BlackWhiteIPListGlobalFilter implements GlobalFilter, Ordered {

    @Autowired
    private RedisUtils redisUtils;

    public static final String WHITE_LIST = "opengate_white_list";
    public static final String BLACK_LIST = "opengate_black_list";

    /**
     * 重新加载数据到缓存的请求URL
     */
    private static final String RELOAD_CACHE_DATA_URL = "/open-gate/reload/gateway.reloadCacheData.service";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Trace.logInfo(Trace.MODULE_FILTER, "BlackWhiteIPListGlobalFilter start filter....");
        String path = exchange.getRequest().getPath().toString();
        // 若是重新加载动态路由请求，则不校验黑白名单
        if (!RELOAD_CACHE_DATA_URL.equals(path)) {
            List<String> whitsList = redisUtils.lGet(WHITE_LIST, 0, -1);
            List<String> blackList = redisUtils.lGet(BLACK_LIST, 0, -1);
            // 请求报文头
            ClogicContext clogicContext = (ClogicContext)exchange.getAttributes().get(Constants.CL_CONTEXT);
            Map<String, Object> dataMap = clogicContext.getDataMap();
            Map<String, Object> headerMap = (Map<String, Object>)dataMap.get(AdapterCanstants.HEADER);
            String channel = (String)headerMap.get("channel");
            String currentIP = getIpAddress(exchange.getRequest());
            Trace.logInfo(Trace.MODULE_FILTER, "BlackWhiteIPListGlobalFilter current ip: {}", currentIP);
            String channelAndIp = channel + "-" + currentIP;
            // Step1: 校验白名单
            if (whitsList.size() == 0 || whitsList == null) {
                throw new AccessControlException(ErrorEnum.accessIpNotInWhiteListError.code(),
                    String.format(ErrorEnum.accessIpNotInWhiteListError.msg(), channelAndIp));
            }
            if (whitsList.size() > 0 && whitsList != null) {
                if (!whitsList.contains(channelAndIp)) {
                    throw new AccessControlException(ErrorEnum.accessIpNotInWhiteListError.code(),
                        String.format(ErrorEnum.accessIpNotInWhiteListError.msg(), channelAndIp));
                }
            }

            // Step2: 校验黑名单
            if (blackList.size() > 0 && blackList != null) {
                if (blackList.contains(currentIP)) {
                    throw new AccessControlException(ErrorEnum.accessIpInBlackListError.code(),
                        String.format(ErrorEnum.accessIpInBlackListError.msg(), currentIP));
                }
            }
        }
        return chain.filter(exchange);
    }

    /**
     * 获取当前请求IP
     * 
     * @param request
     * @return
     */
    private String getIpAddress(ServerHttpRequest request) {
        String ip = RequestUtil.getHeaderValue(request, "X-Forwarded-For");
        Trace.log("FILTER", 1, "getIpAddress(HttpServletRequest) - X-Forwarded-For - String ip={}", new Object[] {ip});
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            Trace.log("FILTER", 1, "代理IP:{}", new Object[] {ip});
            if (ip.length() > 15) {
                String[] ips = ip.split(",");

                for (int index = 0; index < ips.length; ++index) {
                    String strIp = ips[index];
                    if (!"unknown".equalsIgnoreCase(strIp)) {
                        ip = strIp;
                        break;
                    }
                }
            }
        } else {
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = RequestUtil.getHeaderValue(request, "Proxy-Client-IP");
                Trace.log("FILTER", 0, "1.getIpAddress(HttpServletRequest) - Proxy-Client-IP - String ip={}",
                    new Object[] {ip});
            }

            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = RequestUtil.getHeaderValue(request, "WL-Proxy-Client-IP");
                Trace.log("FILTER", 0, "2.getIpAddress(HttpServletRequest) - WL-Proxy-Client-IP - String ip={}",
                    new Object[] {ip});
            }

            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = RequestUtil.getHeaderValue(request, "HTTP_CLIENT_IP");
                Trace.log("FILTER", 0, "3.getIpAddress(HttpServletRequest) - HTTP_CLIENT_IP - String ip={}",
                    new Object[] {ip});
            }

            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = RequestUtil.getHeaderValue(request, "HTTP_X_FORWARDED_FOR");
                Trace.log("FILTER", 0, "4.getIpAddress(HttpServletRequest) - HTTP_X_FORWARDED_FOR - String ip={}",
                    new Object[] {ip});
            }

            if (request.getRemoteAddress() != null) {
                ip = request.getRemoteAddress().getHostString();
                Trace.log("FILTER", 0, "5.getRemoteAddress - String ip={}", new Object[] {ip});
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getURI().getHost();
                Trace.log("FILTER", 0, "6.getIpAddress(HttpServletRequest) - getRemoteAddr - String ip={}",
                    new Object[] {ip});
            }
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    @Override
    public int getOrder() {
        return -15;
    }
}
